-- $Id: gui_terraform.lua 4401 2009-04-16 03:08:42Z google frog $
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
--  Copyright (C) 2008.
--  Licensed under the terms of the GNU GPL, v2 or later.
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

function widget:GetInfo()
  return {
    name      = "Level Terraform GUI",
    desc      = "Shows cost and terrain adjustments.",
    author    = "quantum",
    date      = "July 5, 2008",
    license   = "GNU GPL, v2 or later",
    layer     = 10000,
    enabled   = true,
  }
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

-- Automatically generated local definitions

local GL_LINES           = GL.LINES
local GL_LINE_LOOP       = GL.LINE_LOOP
local glBeginEnd         = gl.BeginEnd
local glCallList         = gl.CallList
local glColor            = gl.Color
local glCreateList       = gl.CreateList
local glDeleteList       = gl.DeleteList
local glDepthTest        = gl.DepthTest
local glLineWidth        = gl.LineWidth
local glVertex           = gl.Vertex
local math_abs           = math.abs
local math_floor         = math.floor
local spGetActiveCommand = Spring.GetActiveCommand
local spGetGroundHeight  = Spring.GetGroundHeight
local spGetMouseState    = Spring.GetMouseState
local spTraceScreenRay   = Spring.TraceScreenRay
local string_format      = string.format

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local pairs, type = pairs, type

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


local skip        = 1  -- increase for better performance, less eye-candy
local tolerance   = 10 -- increase for better performance, less responsiveness
local res         = 50 -- reduce for better performance, lower accuracy
local font        = "LuaUI/Fonts/FreeSansBold_16"
local textColor   = "\255\255\255\255"
local drawVolume  = true
local volumeAlpha = 0.1
local negLine     = {1, 0, 0, 1}
local posLine     = {0, 1, 0, 1}
local negVolume   = {1, 0, 0, volumeAlpha}
local posVolume   = {0, 1, 0, volumeAlpha}
local volumeWidth = 10
local lineWidth   = 2
local depthTest   = false
local levelID     = UnitDefNames['levelterra'].id
local levelCost   = UnitDefNames['levelterra'].buildTime
local blockSize   = 30
local blockHeight = 32 
local levelSize   = 150

local CMD_RAMP    = 39734
local mouseStart  = false


local halfSize    = levelSize/2
local step        = levelSize/res
local groundBox
local toDelete    = {}


local costFactor = 200 -- cost  = costAtZero*2^(heightDelta/costFactor)
local spGetGroundOrigHeight = Spring.GetGroundOrigHeight

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


local function Aprox(x, y)
  local same
  if (type(x) ~= 'table') then
    if (math_floor(x/tolerance) == math_floor(y/tolerance)) then
      same = true
    end
  else
    same = true
    for i=1, #y do
      if (not Aprox(x[i], y[i])) then
        same = nil
      end
    end
  end
  return same
end


local function DrawBottomVertex(v, h)
  if (v[2] > h) then
    glColor(negLine)
  else
    glColor(posLine)
  end
  glVertex(v)
end


local function DrawSide(v, h)
  DrawBottomVertex(v, h)
  glVertex(v[1], h, v[3])
end


local function DrawTopVertex(v, h)
  if (v[2] > h) then
    glColor(negLine)
  else
    glColor(posLine)
  end
  glVertex(v[1], h, v[3])
end


local function Sides(vertices, h)
  DrawSide(vertices[1][1],     h)
  DrawSide(vertices[1][res],   h)
  DrawSide(vertices[res][res], h)
  DrawSide(vertices[res][1],   h)
end


local function Bases(vertices, h)
  for i=1, res, skip do
    DrawBottomVertex(vertices[i][1], h)
  end
  for i=1, res, skip do
    DrawBottomVertex(vertices[res][i], h)
  end
  for i=1, res, skip do
    DrawBottomVertex(vertices[res-i+1][res], h)
  end
  for i=1, res, skip do
    DrawBottomVertex(vertices[1][res-i+1], h)
  end
  DrawTopVertex(vertices[1][1],     h)
  DrawTopVertex(vertices[1][res],   h)
  DrawTopVertex(vertices[res][res], h)
  DrawTopVertex(vertices[res][1],   h)
end


local function Volume(vertices, h)
  for i=1, res, skip do
    for j=1, res, skip do
      local v = vertices[i][j]
      if (v[2] > h) then
        glColor(negVolume)
      else
        glColor(posVolume)
      end
      glVertex(v)
      glVertex(v[1], h, v[3])
    end
  end
end
      

local function ComputeBox(mouse)
  local groundBox = {}
  local costMult = 0
  local vertices = {}
  local h = mouse[2]
  local hDiff = math_abs(spGetGroundOrigHeight(mouse[1], mouse[3])-mouse[2])
  for i=1, res do
    vertices[i] = vertices[i] or {}
    local z = mouse[3]-halfSize+i*step
    for j=1, res do
      local x = mouse[1]-halfSize+j*step
      local y = spGetGroundHeight(x, z)
      vertices[i][j] = {x, y, z}
	  costMult = costMult + math_abs(y-mouse[2])
    end
  end
  costMult = costMult/(res*res)/blockHeight*(levelSize/blockSize)^2*levelCost*math_abs(2^(hDiff/costFactor))

  if (drawVolume) then
    groundBox.volume = glCreateList(glBeginEnd, GL_LINES, Volume, vertices, h)
  end
  groundBox.sides  = glCreateList(glBeginEnd, GL_LINES, Sides, vertices, h)

  groundBox.bases  = glCreateList(glBeginEnd, GL_LINE_LOOP, Bases, vertices, h)
  groundBox.text   = string_format(textColor.."Cost: %d", costMult*0.001)
  groundBox.mouse  = mouse
  toDelete[groundBox.sides]  = true
  toDelete[groundBox.volume] = true
  toDelete[groundBox.bases]  = true
  return groundBox
end


local function StopDrawing()
  if (groundBox) then
    for list in pairs(toDelete) do
      glDeleteList(list)
    end
    toDelete  = {}
    groundBox = nil
  end
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

local function InMinimap(x,y)
  local posx,posy,sizex,sizey,minimized,maximized = Spring.GetMiniMapGeometry()
  rx,ry = (x-posx)/sizex,(y-posy)/sizey
  return (not (minimized or maximized)) and
         (rx>=0)and(rx<=1)and
         (ry>=0)and(ry<=1),rx,ry
end
local function MinimapToWorld(rx,ry)
  if (rx>=0)and(rx<=1)and
     (ry>=0)and(ry<=1)
  then
    local mapx,mapz = Game.mapSizeX*rx,Game.mapSizeZ-Game.mapSizeZ*ry
    local mapy = Spring.GetGroundHeight(mapx,mapz)
    return {mapx,mapy,mapz}
  else
    return {-1,-1,-1}
  end
end

function widget:DrawScreen()
  local _, activeCommand = spGetActiveCommand()
  if (activeCommand == -levelID) then
    local mouseX, mouseY = spGetMouseState()
    local category, mouse  = spTraceScreenRay(mouseX, mouseY, true)
    if (category == "ground") then
      if (groundBox and not Aprox(groundBox.mouse, mouse) or not groundBox) then
        groundBox = ComputeBox(mouse)
      end
    else
     StopDrawing()
    end
  else
    StopDrawing()
  end
  if (groundBox) then
    local mouseX, mouseY = spGetMouseState()
    fontHandler.UseFont(font)
    fontHandler.DrawCentered(groundBox.text, mouseX, mouseY+10)
  end

  if (activeCommand == CMD_RAMP) then
  
    local x, y, b1 = spGetMouseState() --b1 = left button pressed?
    if b1 then		  
	  if mouseStart == false then
	    inMinimap,minix, miniy = InMinimap(x,y)
        if inMinimap then
          mouseStart = MinimapToWorld(minix,miniy)
        else
          _, mouseStart = spTraceScreenRay(x, y, true) --cursor on world pos]
		end
	  else	    
	    local _, mouseCur = spTraceScreenRay(x, y, true)	
		inMinimap,minix, miniy = InMinimap(x,y)
        if inMinimap then
          mouseCur = MinimapToWorld(minix,miniy)
		end
		if mouseCur and mouseStart then
		  local dis = ((mouseStart[1]-mouseCur[1])^2+(mouseStart[3]-mouseCur[3])^2)^0.5
		  if dis ~= 0 then
		    local grad = math_abs(mouseStart[2]-mouseCur[2])/dis		
		    fontHandler.UseFont(font)
		    local text 
		    if grad < 0.5 then
		      text = string_format("\255\100\255\100 ".." All Passable ")
		    elseif grad < 1.25 then
		      text = string_format("\255\200\200\100 ".." Bot Passable ")
		    else
		      text = string_format("\255\255\100\100 " .." In-Passable ")
		    end
		    fontHandler.DrawCentered(text, x, y+10)
		  end
		end
	  end	  
    else
      mouseStart = false
	end
	
  end 
end


function widget:DrawWorld()
  if (groundBox) then
    glDepthTest(depthTest)
    glLineWidth(lineWidth)
    glCallList(groundBox.sides)
    glCallList(groundBox.bases)
    if (drawVolume) then
      glLineWidth(volumeWidth)
      glCallList(groundBox.volume)
    end
    glLineWidth(1)
    glDepthTest(false)
  end
  
end

function widget:Initialize()

  if(Spring.GetModOptions or Spring.GetModOptions().deathmode or (Spring.GetModOptions().deathmode == "chickens")) then

	widgetHandler:RemoveWidget(self)
  end
end

function widget:ShutDown()
  StopDrawing()
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------